
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;

using Swaf;
using Swaf.BizRule;
using Swaf.BizObj;
using Swaf.Container;

using MediaCache;
using Syrinx;

using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Core;

namespace MediaCacheAdmin
{
	/// <summary>Provides the editing access bizrules for the MediaCacheAdmin feature of the framework gallery.
    /// These rules are specifically for the media items contains within the media cache and not for managing the
    /// media cache itself.
    /// The read only bizrules are contained within the MediaCache.BusinessRules class.
    /// All bizrules in this class will throw a MediaCacheAdminException when a failure occurs.
    /// </summary>
	public class BusinessRules : Syrinx.BaseBizRules
	{
		public void backupCompanyMediaCaches(IApplication app, IRuleContext context, string treeName, IBizObj deployInfo, ZipOutputStream zOut)
		{
			if (!(bool)deployInfo.get("fullCompanyBackup") && !(bool)deployInfo.get("includeImageCache"))
				return;

			//We need to change the active company id to the company that the backup is for so that
			//any reference to the activeCompanyId in the following code will point to the correct company.
			Swaf.BGuid.Guid oldCmpId = ActiveCompanyId;
			try
			{
				foreach (IBizObj company in deployInfo.get("backupCompanies") as IBizObjList)
				{
					ActiveCompanyId = company.get("id") as Swaf.BGuid.Guid;
					IResults rc = context.RuleGroup.execute(context, "getCompanyMediaCacheLibraries");
					IBizObjList mediaCaches = rc.CallResults as IBizObjList;
					if (mediaCaches != null)
						foreach (IBizObj mediaCache in mediaCaches)
						{
							bool getAllImagesInCache = true;
							rc = context.RuleGroup.execute(context, "getImageList", mediaCache.get("id"), getAllImagesInCache);
							IBizObjList images = rc.CallResults as IBizObjList;
							foreach (IBizObj image in images)
							{
								string imageName = (string)image.get("name");
								string imageLoc = (string)image.get("location");

								// images.cacheCatalogName.ImgAjdustName.path.imageName
								string fullImageName = string.Format("images.{0}.{1}", mediaCache.get("id"), imageName);
								rc = context.RuleGroup.execute(context, "getImage", fullImageName);
								MediaStream media = (MediaStream)rc.CallResults;

								if (media != null)
								{
									string mediaZipEntryName = imageLoc + media.Name;
									if (mediaZipEntryName.EndsWith(".image"))
										mediaZipEntryName = mediaZipEntryName.Substring(0, mediaZipEntryName.Length - 6);
									mediaZipEntryName = mediaZipEntryName.Replace('/', '\\').Replace("\\\\", "\\");

									zOut.PutNextEntry(new ZipEntry(string.Format("data{0}{1}{0}Images{0}{2}{0}{3}",
										Path.DirectorySeparatorChar, company.get("shortName"), mediaCache.get("id"), mediaZipEntryName)));

									byte[] bytes = media.Bytes;
									for (int i = 0; i < bytes.Length; i++)
										zOut.WriteByte(bytes[i]);

									media.Dispose();
								}
							}
						}
				}
			}
			finally
			{
				ActiveCompanyId = oldCmpId;
			}
		}

		public void postExtendMediaCacheexecuteCompanyBackup(IApplication app, IRuleContext context, object returnValue, object[] inputParams)
		{

			try
			{
				INameAddressSupport deployInfo = inputParams[2] as INameAddressSupport;
				ZipOutputStream zOut = inputParams[3] as ZipOutputStream;

				IResults rc = context.RuleGroup.execute(context, "backupCompanyMediaCaches", "Articles", deployInfo, zOut);
			}
			catch (Exception e)
			{
				throw new MediaCacheAdminException("error executing postExtendProductCreateDeployment.", e);
			}
		}

		public void postExtendMediaCacheexecuteCompanyRestore(IApplication app, IRuleContext context, object returnValue, object[] inputParams)
		{
			try
			{
				INameAddressSupport deployInfo = inputParams[2] as INameAddressSupport;
				ZipFile zipFile = inputParams[3] as ZipFile;
				IResults rc = context.RuleGroup.execute(context, "restoreCompanyMediaCaches", deployInfo, zipFile);
				if (rc.ExceptionInfo != null)
					throw rc.ExceptionInfo;
			}
			catch (Exception e)
			{
				throw new MediaCacheAdminException("error executing postExtendProductCreateDeployment.", e);
			}
		}

		public void restoreCompanyMediaCaches(IApplication app, IRuleContext context, IBizObj bkInfo, ZipFile zipFile)
		{
			IBizObjList restoreCompanies = (IBizObjList)bkInfo.get("restoreCompanies");
			IBizObjList backupCompanies = (IBizObjList)bkInfo.get("backupCompanies");
			for (int pos = 0; pos < restoreCompanies.numElements; ++pos)
			{
				IBizObj restoreCompany = restoreCompanies[pos];
				IBizObj backupCompany = backupCompanies.numElements == 1 ? backupCompanies[0] : backupCompanies[pos];
				//                                         shortName             cacheName     fileName
				Regex pathRegex = new Regex(@"^data[\\/]([^/\\]+)[\\/]images[\\/]([^/\\]+)[\\/](.*)", RegexOptions.IgnoreCase);
				Match m;
				IBizObj bizObj = app.bizObjMgr.create("MediaItem");
				foreach (ZipEntry entry in zipFile)
				{
					string entryName = entry.Name;
					if (entry.IsFile && (m = pathRegex.Match(entryName)).Success && m.Groups[1].Value.CompareTo(backupCompany.get("shortName")) == 0)
					{
						string mediaCacheName = m.Groups[2].Value;
						string fileName = m.Groups[3].Value;
						if (mediaCacheName.StartsWith(m.Groups[1].Value, StringComparison.OrdinalIgnoreCase))
							mediaCacheName = restoreCompany.get("shortName") + mediaCacheName.Substring(m.Groups[1].Length);

						bizObj.put("file.name", fileName);
						bizObj.put("file.data", new Swaf.Binary(zipFile.GetInputStream(entry), true));
						IResults rc = context.RuleGroup.execute(context, "addImage", mediaCacheName, bizObj);
					}
				}
			}
		}


		public object getMediaCacheBackupRestoreTemplate(IApplication app, IRuleContext context, object resultValue, object[] inputParams)
		{
			string template = "MediaCache.Gui.AspNet.BaseDeploymentOptions";
			if (resultValue != null)
				return resultValue.ToString().Length == 0 ? template
					: new StringBuilder(50).Append(resultValue.ToString()).Append(",").Append(template).ToString();
			return template;
		}



		public void updateCompanyImageCacheLimits(IApplication app, IRuleContext context, IBizObj limit)
		{
			try
			{
				//context.put("limit",limit);
				if (limit != null)
					app.appData.put(string.Format("DA.MediaCacheAdmin.{0}", limit.isNew ? "AddCompanyLimit" : "UpdateCompanyLimit"), limit);
				else
					throw new MediaCacheAdminException("Error executing updateCompanyImageCacheLimits. Limit is null.");
			}
			catch (Exception e)
			{
				throw new MediaCacheAdminException("Error executing updateCompanyImageCacheLimits.", e);
			}
		}
		
		/// <summary>
        /// Will save the given image into the specified media cache.
        /// </summary>
        /// <param name="catalogName">The name of the catalog the image should be add to.</param>
        /// <param name="image">The Image bizobj that contains the image data, directory name and file
        /// name needed to save the image.</param>
		public void addImage(IApplication app, IRuleContext context, string catalogName, IBizObj image)
		{
            if (image == null)
                throw new MediaCacheAdminException("The parameter image cannot be null");

            app.Log.println("MediaCacheAdmin.addImage: {0}", image.get("name"));
            try
            {
                BaseImageCache ic = app.appData.get("images." + catalogName) as BaseImageCache;
                if (ic != null)
                    ic.addImage(image);
                else
                    throw new MediaCacheAdminException("Unable to add image into media cache named " + catalogName);
            }
            catch (MediaCacheAdminException)  { throw;  }
            catch (Exception e)
            {
                throw new MediaCacheAdminException("Error while adding image into media cache named " + catalogName, e);
            }
		}

        /// <summary>
        /// Will save each image in the bizobj list of images provided into the media cache specified.
        /// </summary>
        /// <param name="catalogName">The name of the media cache the images should be added to.</param>
        /// <param name="images">The bizobj list of images that each contain the image data, directory name and
        /// file name needed to save the image.</param>
        public void addImages(IApplication app, IRuleContext context, string catalogName, IBizObjList images)
        {
            if (images == null)
                throw new MediaCacheAdminException("The parameter image cannot be null");

            foreach (IBizObj image in images)
            {
                IResults rc = app.ruleMgr.execute("addImage", catalogName, image);
                if (rc.ExceptionInfo != null)
                    throw rc.ExceptionInfo;
            }
        }

		public void moveImagesToDirectory(IApplication app, IRuleContext context, string catalogName, IBizObjList images, string newDir)
		{
			BaseImageCache ic = app.appData.get("images." + catalogName) as BaseImageCache;
			if (ic != null)
			{
				foreach (IBizObj image in images)
				{
					string imgName = (string)image.get("name");
					if (imgName.EndsWith(".image"))
						imgName = imgName.Substring(0, imgName.Length - 6);
					ic.moveImage(imgName, newDir.Replace(':', Path.DirectorySeparatorChar));
				}
			}
			else
				throw new MediaCacheAdminException("Unable to add image into media cache named " + catalogName);
		}



        /// <summary>
        /// Will delete the image specified from the given media cache storage.
        /// </summary>
        /// <param name="catalogName">The name of the catalog the image should be deleted from</param>
        /// <param name="dummy">Specified to avoid the rulemanager bug with the 2nd param being a string.</param>
        /// <param name="name">The name of the image to be removed, which should include the directory path.</param>
		public void removeImage(IApplication app, IRuleContext context, string catalogName, bool dummy, string name)
		{
            if (catalogName == null || name == null)
                throw new MediaCacheAdminException("The parameters catalogName and name cannot be null");

            BaseImageCache ic = app.appData.get("images." + catalogName) as BaseImageCache;
            if (ic != null)
            {
				if (name.EndsWith(".image"))
					name = name.Substring(0, name.Length - 6);
                if (!ic.deleteImage(name))
                    throw new MediaCacheAdminException("Failure deleting the image " + name + " from the media cache named " + catalogName);
            }
            else
                throw new MediaCacheAdminException("Could not find a media cache with the name " + catalogName);
		}

		public void removeImages(IApplication app, IRuleContext context, string catalogName, IBizObjList images)
		{
			foreach (IBizObj img in images)
				context.RuleGroup.execute(context, "removeImage", catalogName, true, img.get("name"));
		}

		/// <summary>
		/// This bizrule will update the given media cache based on the given company by updating the 
		/// data store and calling the updateImageCacheScaling and updateMediaCache bizrules
		/// from the MediaCacheAdmin feature.  This method will calculate the media cache's id based
		/// on the media cache name and the company name, which means the id could have changed.  
		/// When a media cache id changes, its associated catalog entry is renamed and if its image
		/// store is within the $BaseMediaStore$ directory the directory will be renamed.  That work
		/// is done by the MediaCacheAdmin feature bizrules.
		/// </summary>
		/// <remarks>This bizrule takes a company bizobj because it is called from the post extends
		/// updateCompany bizrule which is given a company.</remarks>
		/// <param name="cacheInfo">The media cache to update from</param>
		/// <param name="company">The company the media cache is associated with.</param>
		/// <returns>The new id of the media cache based on this method's calculations.</returns>
		public string updateCompanyImageCacheLibrary(IApplication app, IRuleContext context, IBizObj cacheInfo)
		{
			IResults rc = null;
			if (cacheInfo == null)
				throw new MediaCacheAdminException("The various bizobjs passed must not be null");

			if (cacheInfo.isNew)
			{
				rc = context.RuleGroup.execute(context, "createNewCompanyImageCacheLibrary", cacheInfo.get("name"), null);
				if (rc.ExceptionInfo != null)
					throw rc.ExceptionInfo;
				else
					return rc.CallResults as string;
			}
			try
			{
				IBizObj company = ActiveCompany;
				string newId = prefixWithCompanyName(cacheInfo.get("name").ToString(), company);
				string oldId = cacheInfo.get("id").ToString();
				FlexiMap m = new FlexiMap();
				IBizObjList scalings = cacheInfo.get("scalings") as IBizObjList;

				if (scalings.numElements == 0)
				{
					rc = app.ruleMgr.execute("getImageCacheDetails", oldId);
					if (rc.ExceptionInfo != null)
						throw rc.ExceptionInfo;
					cacheInfo = rc.CallResults as IBizObj;
				}
				else
				{
					// update only scalings if change is via mediaCache update
					rc = app.ruleMgr.execute("updateImageCacheScaling", cacheInfo);
					if (rc.ExceptionInfo != null)
						throw rc.ExceptionInfo;
				}
				if (newId != oldId)
				{
					cacheInfo.put("id", newId);
					rc = app.ruleMgr.execute("updateMediaCache", oldId, cacheInfo);
					if (rc.ExceptionInfo != null)
						throw rc.ExceptionInfo;
					m["oldid"] = oldId;
					m["cache"] = cacheInfo;
					app.appData.put("Data.GalleryAdmin.UpdateImageCachePriviledge", m);
					app.appData.put("Data.GalleryAdmin.UpdateMediaCacheLibrary", m);
					app.appData.put("Data.GalleryAdmin.UpdateAlbumGroupImageCache", m);

				}
				if (scalings.numElements > 0 && cacheInfo.get("isDefault").Equals(true))
				{
					m["id"] = cacheInfo.get("id");
					m["companyId"] = ActiveCompanyId;
					app.appData.put("Data.GalleryAdmin.ResetDefaultLibrary", m);
				}
				return newId;
			}
			catch (Exception e)
			{
				throw new MediaCacheAdminException("Unable to update company image cache library", e);
			}
		}
		/// <summary>
		/// this method is post extension bizrule of the UserManagement.createCompany bizrule
		/// </summary>
		public IBizObj executePostCompanySetup(IApplication app, IRuleContext context, object returnValue, object[] inputParams)
		{
			try
			{
				Debug.Assert(returnValue != null);
				IBizObj company = returnValue as IBizObj;
				createNewCompanyImageCacheLibrary(app, context, null, company);
				return company;
			}
			catch (Exception e)
			{
				throw new MediaCacheAdminException("Error executing executePostCompanySetup bizrule.", e);
			}
		}


		public void deleteMediaDirectory(IApplication app, IRuleContext context, IBizObj mediaDir)
		{
			MediaCache.BaseImageCache ic = (MediaCache.BaseImageCache)app.appData.get("images." + mediaDir.get("cacheName"));
			string rootPath = app.resMgr.resolve(ic.CacheConfig.get("origionalImagesLocation").ToString()) + Path.DirectorySeparatorChar;
			string path = app.resMgr.resolve(Cmn.pathCombine(ic.CacheConfig.get("origionalImagesLocation").ToString(),
				((string)mediaDir.KeyValue).Replace(':', Path.DirectorySeparatorChar)));
			if (rootPath.CompareTo(path) != 0)
			{
				DirectoryInfo d = new DirectoryInfo(path);
				if (d.Exists)
					d.Delete(true);
			}
			else
			{
				//TODO: give response to UI that the root directory cannot be deleted.
			}
		}

		public void updateMediaDirectory(IApplication app, IRuleContext context, IBizObj mediaDir)
		{
			MediaCache.BaseImageCache ic = (MediaCache.BaseImageCache)app.appData.get("images." + mediaDir.get("cacheName"));

			string path = app.resMgr.resolve(Cmn.pathCombine(ic.CacheConfig.get("origionalImagesLocation").ToString(),
				((string)mediaDir.KeyValue).Replace(':', Path.DirectorySeparatorChar)));
			string newPath = app.resMgr.resolve(Cmn.pathCombine(ic.CacheConfig.get("origionalImagesLocation").ToString(),
				((string)mediaDir.get("parentName") + ":" + mediaDir.get("name")).Replace(':', Path.DirectorySeparatorChar)));
			if (mediaDir.isNew)
			{
				DirectoryInfo d2 = new DirectoryInfo(newPath);
				if (!d2.Exists)
					d2.Create();
				else
					throw new MediaCacheAdminException("Directory already exists");
			}
			else
			{
				DirectoryInfo d = new DirectoryInfo(path);
				if (d.Exists)
					d.MoveTo(newPath);
			}
		}

		/// <summary>
		/// Will create a new Media Cache for the given company. This bizrule calculates the
		/// media cache id based on the starting name of "images", but will postfix a number to
		/// it to create a unique id within the company's existing media caches if needed.
		/// </summary>
		/// <remarks>This bizrule takes a company bizobj because it can be called during the creation
		/// of a new company, before that company becomes active.  Also, if the data store is blank
		/// and the checkRootCompany bizrule is called, this method will be called even before the
		/// anonymous user is set as the current user.</remarks>
		/// <param name="company">The company to create the image cache for, which can be null.  
		/// If null, this bizrule will use the current user's active company.</param>
		/// <returns>The id of the newly created media cache.</returns>
		public string createNewCompanyImageCacheLibrary(IApplication app, IRuleContext context, string newCacheName, IBizObj company)
		{
			try
			{
				IResults rc = null;
				string basedOnId = null;
				string newId = "images";
				string newName = newId;

				if (company == null)
					company = ActiveCompany;

				if (!Cmn.IsEmpty(newCacheName))
					newId = newName = newCacheName;

				context.put("companyId", company.get("id"));
				IBizObj mediaCache = app.appData.get("DA.MediaCache.GetCompanyDefaultMediaCache", context) as IBizObj;
				if (mediaCache != null)
					basedOnId = mediaCache.get("id").ToString();

				//Check for duplicate names
				context.put("matchName", newName);
				IBizObjList mediaCaches = app.appData.get("Data.GalleryAdmin.GetMatchingCompanyMediaCache", context) as IBizObjList;
				if (mediaCaches != null && mediaCaches.numElements > 0)
					for (int c = 0; c < 100; ++c)
					{
						newName = newId + c;
						if (mediaCaches.search("name", newName, false) == null)
							break;
					}

				newId = prefixWithCompanyName(newName, company);

				rc = app.ruleMgr.execute("addMediaCache", newId, true, newName, basedOnId);
				if (rc.ExceptionInfo != null)
					throw rc.ExceptionInfo;

				context.put("id", newId);
				context.put("libraryName", newName);
				context.put("isDefault", rc.CallResults);
				app.appData.put("DA.MediaCacheAdmin.CreateCacheImageLibrary", context);
				return newId;
			}
			catch (Exception e)
			{
				throw new MediaCacheAdminException("error executing createNewCompanyImageCacheLibrary", e);
			}
		}
		protected string prefixWithCompanyName(string strValue, IBizObj company)
		{
			if (company == null)
				return prefixWithCompanyName(strValue);
			return cleanUpFileName(company.get("shortName").ToString()) + strValue;
		}
		protected string prefixWithCompanyName(string strValue)
		{
			IApplication app = Swaf.Application.currentApp;
			IResults rc = app.ruleMgr.execute("getCompany", ActiveCompanyId);
			if (rc.ExceptionInfo != null)
				throw rc.ExceptionInfo;
			IBizObj company = rc.CallResults as IBizObj;
			if (company != null)
				return prefixWithCompanyName(strValue, company);
			else
				return strValue;
		}
		protected string cleanUpFileName(string name)
		{
			return name.Replace(".", "").Replace("'", "")
				.Replace("/", "").Replace("\\", "").Replace(" ", "").Replace("  ", "");
		}


	}
}
